﻿using Com.Lancetinc.Evs.Communications;
using Com.Lancetinc.Evs.Events;
using Com.Lancetinc.Evs.Helpers;
using Com.Lancetinc.Evs.Models;
using Com.Lancetinc.Evs.Services;
using Com.Lancetinc.Evs.ViewModels;
using Com.Lancetinc.Evs.Windows;
using Enterwell.Clients.Wpf.Notifications;
using MahApps.Metro.IconPacks;
using MaterialDesignThemes.Wpf;
using Prism.Commands;
using Prism.Events;
using System;
using System.Linq;
using System.Windows;
using System.Windows.Input;

namespace Com.Lancetinc.Evs
{
    public class MainWindowViewModel : ViewModelBase
    {
        public InstrumentViewModel InstrumentVm { get; }

        public MainWindowControlButtonViewModel ControlButtonVm { get; set; }

        public ExperInfo ExperInfo { get; }

        public MainWindowViewModel()
        {
            InstrumentVm = new InstrumentViewModel();
            ControlButtonVm = new MainWindowControlButtonViewModel(this);

            CreateCommands();
            RegisterEvents();
            ExperInfo = ExperManager.Default.CreateExper();
        }

        private void RegisterEvents()
        {
            ActiveApp.Instance.InstManager.StateChanged += InstManager_StateChanged;
            ActiveApp.Instance.InstManager.PacketReceived += InstManager_PacketReceived;
            KnownAppEvents.RecordStateChangedEvent.Subscribe(InstrumentRecordStateChanged, ThreadOption.UIThread);
        }

        private bool _isRecording = false;
        public bool IsRecording
        {
            get => _isRecording;
            set => SetProperty(ref _isRecording, value);
        }

        private bool _isFullScreen = false;

        public bool IsFullScreen
        {
            get => _isFullScreen;
            set => SetProperty(ref _isFullScreen, value);
        }

        private void InstrumentRecordStateChanged(RecordStateChangedEventArgs args)
        {
            IsRecording = args.Recording;
            this.Refresh();
        }

        private void InstManager_StateChanged(object sender, InstrumentInfoChangedEventArgs e)
        {
            OnInstManagerStateChanged(e);

            InstrumentVm.OnInstManagerStateChanged(e);
        }

        private void InstManager_PacketReceived(object sender, PacketReceivedEventArgs e)
        {
            InstrumentVm.OnInstManagerPacketReceived(e);
        }

        #region 消息相关

        public SnackbarMessageQueue MessageQueue { get; } = new() { DiscardDuplicates = true };
        public ICommand ShowToastCommand { get; private set; }
        /// <summary>
        /// 正下方显示提示消息，默认3秒后消失
        /// </summary>
        /// <param name="msg"></param>
        /// <param name="duration">停留时间</param>
        public void ShowToast(object msg, TimeSpan? duration = null)
        {
            MessageQueue.Enqueue(msg, null, null, null,
                promote: false,
                neverConsiderToBeDuplicate: false,
                durationOverride: duration);
        }

        private void ExecuteShowToastCommand(object msg)
        {
            if (msg == null)
                return;
            ShowToast(msg);
        }

        /// <summary>
        /// 通知栏
        /// </summary>
        public INotificationMessageManager NotificationMessageManager
        {
            get;
        } = new NotificationMessageManager();

        public void ShowPinnedError(string errMsg, bool showTime = true, TimeSpan? dismissDelay = null, object tag = null)
        {
            if (NotificationMessageManager.CurrentMessages.Any(x => x.Message == errMsg))
            {
                return; // 不显示同样消息
            }

            var msg = showTime ? errMsg : "[" + DateTime.Now.ToLongTimeString() + "] " + errMsg;

            tag ??= errMsg;
            var builder = NotificationMessageManager.CreateMessage()
                .Animates(true)
                .AnimationInDuration(1)
                .AnimationOutDuration(1)
                .Accent("#E0A030")
                .Background("#45455f")
                .Foreground("#FFE6A23C")
                //.HasBadge(I18nHelper.Default.GetMessage("MsgBox.Warning"))
                .HasTag(tag)
                .HasMessage(msg);

            builder.SetAdditionalContentOverBadge(new PackIconLevs
            {
                Kind = PackIconLevsKind.Warning,
                Height = 18,
                Width = 18
            });
            if (dismissDelay.HasValue)
            {
                builder.Dismiss().WithDelay(dismissDelay.Value);
            }

            builder.Dismiss().WithButton(new PackIconLevs
            {
                Kind = PackIconLevsKind.Close,
                Height = 16,
                Width = 16,
                ToolTip = I18nHelper.Default.GetMessage("Button.Close")
            }, button => { });

            builder.Queue();
        }

        public void ClearPinnedError(string errMsg, object tag = null)
        {
            tag ??= errMsg;
            var existsMsg = NotificationMessageManager.CurrentMessages
                .FirstOrDefault(x => x.Tag == tag);
            if (existsMsg == null)
                return;

            NotificationMessageManager.Dismiss(existsMsg);
        }

        #endregion

        #region Comamnds
        /// <summary>
        /// 系统设置
        /// </summary>
        public DelegateCommand SettingCommand { get; private set; }
        /// <summary>
        /// 关于
        /// </summary>
        public ICommand AboutCommand { get; private set; }
        /// <summary>
        /// 视频设置
        /// </summary>
        public DelegateCommand VideoSettingCommand { get; private set; }
        /// <summary>
        /// 关闭程序
        /// </summary>
        public DelegateCommand ShutDownCommand { get; private set; }

        private void CreateCommands()
        {
            SettingCommand = new DelegateCommand(ExecuteSettingCommand, CanExecuteSettingCommand);
            AboutCommand = new DelegateCommand(ExecuteAboutCommand);
            VideoSettingCommand = new DelegateCommand(ExecuteVideoSettingCommand, CanExecuteVideoSettingCommand);
            ShutDownCommand = new DelegateCommand(ExecuteShutDownCommand, CanExecuteShutDownCommand);
            ShowToastCommand = new DelegateCommand<object>(ExecuteShowToastCommand);
        }

        private void RefreshButtonState()
        {
            SettingCommand.RaiseCanExecuteChanged();
            VideoSettingCommand.RaiseCanExecuteChanged();
            ShutDownCommand.RaiseCanExecuteChanged();
        }

        private bool CanExecuteVideoSettingCommand()
        {
            return !IsRecording &&
                   ActiveApp.Instance.InstManager.IsOnline &&
                   ActiveApp.Instance.Instrument.EndoscopicConnected;
        }

        private void ExecuteSettingCommand()
        {
            MainWindow.Instance.CtrlSettingFlyout.ShowSetting();
        }

        private void ExecuteAboutCommand()
        {
            MainWindow.Instance.CtrlSettingFlyout.ShowAbout();
        }

        private bool CanExecuteSettingCommand()
        {
            return !IsRecording;
        }

        private void ExecuteShutDownCommand()
        {
            MainWindow.Instance.Close();
        }

        private bool CanExecuteShutDownCommand()
        {
            return !IsRecording;
        }

        private void ExecuteVideoSettingCommand()
        {
            if (ActiveApp.Instance.Instrument.VideoRecording)
            {
                // 实验中，不可编辑视频设置
                MessageHelper.ShowWarningByMsgId("CannotEditExperWhenRecording");
                return;
            }

            if (!ActiveApp.Instance.Instrument.EndoscopicConnected || !ActiveApp.Instance.InstManager.IsOnline)
            {
                // 内窥镜未连接!
                MessageHelper.ShowWarningByMsgId("EndoscopeNotConnected");
                return;
            }

            var wnd = new VideoSettingWindow
            {
                Owner = Application.Current.MainWindow,
            };
            wnd.ShowDialog();
        }

        #endregion

        private void OnInstManagerStateChanged(InstrumentInfoChangedEventArgs e)
        {
            var ins = e.Instrument;
            // 录像中设备断开连接
            if (this.IsRecording && (!ins.InstrumentConnected || !ins.EndoscopicConnected))
            {
                WpfHelper.ExecLaterEx("RequireStopVideoRecord", () =>
                {
                    KnownAppEvents.PublishRequireStopVideoRecord(RequireStopReason.VideoDisconnected);
                    ShowPinnedError(I18nHelper.Default.GetMessage("EndoscopeDisConnected"),
                        dismissDelay: TimeSpan.FromSeconds(5));
                }, 100);
            }
            this.Refresh();
        }

        public override void Refresh()
        {
            base.Refresh();

            RefreshButtonState();
            ControlButtonVm.Refresh();
        }
    }

    public class MainWindowControlButtonViewModel : ViewModelBase
    {
        private MainWindowViewModel MainVm { get; }

        /// <summary>
        /// 显示视频回放页面
        /// </summary>
        public DelegateCommand ShowVideoHistoryCommand { get; private set; }
        public ICommand CloseShowVideoHistory { get; private set; }
        /// <summary>
        /// 显示病人信息录入页面
        /// </summary>
        public DelegateCommand ShowPatientDataInputCommand { get; private set; }

        private bool _isShowVideoHistory = false;
        public bool IsShowVideoHistory
        {
            get => _isShowVideoHistory;
            set => SetProperty(ref _isShowVideoHistory, value);
        }

        private bool _isShowPatientDataInput = false;
        public bool IsShowPatientDataInput
        {
            get => _isShowPatientDataInput;
            set => SetProperty(ref _isShowPatientDataInput, value);
        }

        private bool _isShowCapturedImage = false;
        public bool IsShowCapturedImage
        {
            get => _isShowCapturedImage;
            set => SetProperty(ref _isShowCapturedImage, value);
        }

        private int _capturedImageCount;
        public int CapturedImageCount
        {
            get => _capturedImageCount;
            set
            {
                if (SetProperty(ref _capturedImageCount, value))
                {
                    RaisePropertyChanged(nameof(CapturedScreenButtonText));
                }
            }
        }

        public string LedIconName
        {
            get
            {
                var isLedOn = MainVm.InstrumentVm.IsLedOn == true;
                return isLedOn ? PackIconLevsKind.LedOff.ToString() : PackIconLevsKind.LedOnFill.ToString();
            }
        }

        public string LedIconName2
        {
            get
            {
                var isLedOn = MainVm.InstrumentVm.IsLedOn == true;
                return isLedOn ? PackIconLevsKind.LedOff.ToString() : PackIconLevsKind.LedOn.ToString();
            }
        }

        public string LedText
        {
            get
            {
                var isLedOn = MainVm.InstrumentVm.IsLedOn == true;
                return isLedOn ? I18nHelper.Default.GetMessage("Button.LedOff") : I18nHelper.Default.GetMessage("Button.LedOn");
            }
        }

        public bool IsRecording => MainVm.IsRecording;

        public string CapturedScreenButtonText => I18nHelper.Default.GetMessageWithArgs("Button.CapturedImage", _capturedImageCount);

        public MainWindowControlButtonViewModel(MainWindowViewModel mainVm)
        {
            MainVm = mainVm;
            CreateCommands();
            RegisterEvents();
        }

        private void RegisterEvents()
        {
            KnownAppEvents.CaptureImageChangedEvent.Subscribe(OnCaptureScreenSuccessEvent, ThreadOption.UIThread);
            KnownAppEvents.InstrumentLedStateChangedEvent.Subscribe(OnInstrumentLedStateChangedEvent, ThreadOption.UIThread);
            KnownAppEvents.VideoRecordStoppedEvent.Subscribe(OnVideoRecordStoppedEvent, ThreadOption.UIThread);
        }

        private void OnCaptureScreenSuccessEvent(CaptureImageChangedEventArgs args)
        {
            this.CapturedImageCount = args.ExperInfo.Images.Count;
            if (CapturedImageCount == 0)
                return;

            this.Refresh();

            if (args.Reason == 0)
            {
                KnownAppEvents.PublishRequireShowCapturedImage(args.ExperInfo, args.ExperInfo.Images.Last(), TimeSpan.FromSeconds(3));
            }
        }

        private void OnVideoRecordStoppedEvent(VideoRecordStoppedEventArgs obj)
        {
            CapturedImageCount = 0;
            this.Refresh();
        }

        private void OnInstrumentLedStateChangedEvent(InstrumentLedStateChangedEventArgs obj)
        {
            this.Refresh();
        }

        private void CreateCommands()
        {
            ShowVideoHistoryCommand = new DelegateCommand(ExecuteShowVideoHistoryCommand, CanExecuteShowVideoHistoryCommand);
            CloseShowVideoHistory = new DelegateCommand(ExecuteCloseShowVideoHistory);
            ShowPatientDataInputCommand = new DelegateCommand(ExecuteShowPatientDataInputCommand);
        }

        private void RefreshButtonState()
        {
            ShowVideoHistoryCommand.RaiseCanExecuteChanged();
            ShowPatientDataInputCommand.RaiseCanExecuteChanged();
        }

        private bool CanExecuteShowVideoHistoryCommand()
        {
            return !MainVm.IsRecording;
        }

        private void ExecuteShowVideoHistoryCommand()
        {
            IsShowVideoHistory = true;
        }

        private void ExecuteCloseShowVideoHistory()
        {
            IsShowVideoHistory = false;
        }

        private void ExecuteShowPatientDataInputCommand()
        {
            IsShowPatientDataInput = true;
        }

        public override void Refresh()
        {
            base.Refresh();
            RefreshButtonState();
        }
    }

    public class InstrumentViewModel : ViewModelBase
    {
        private bool? _isLedOn = null;

        public bool? IsLedOn => !Connected ? null : _isLedOn;

        public bool Connected => Instrument.InstrumentConnected;

        public IInstrumentState Instrument => ActiveApp.Instance.InstManager.Instrument;

        public InstrumentViewModel()
        {
        }

        public void OnInstManagerStateChanged(InstrumentInfoChangedEventArgs e)
        {
            if (string.IsNullOrEmpty(e.Instrument.HcSerialNo))
                return;

            if (e.ChangedPropertyNames.Contains(PropertyConsts.LedBrightness))
            {
                var isLedOn = e.Instrument.LedBrightness > 0;
                if (_isLedOn != isLedOn)
                {
                    _isLedOn = isLedOn;
                    KnownAppEvents.PublishInstrumentLedStateChanged(e.Instrument);
                }
            }

            if (e.ChangedPropertyNames.Contains(PropertyConsts.ErrorCode))
            {
                KnownAppEvents.PublishInstrumentErrorStateChanged(e.Instrument);
            }

            this.Refresh();
        }

        public void OnInstManagerPacketReceived(PacketReceivedEventArgs e)
        {
            var videoRecording = ActiveApp.Instance.InstManager.Instrument.VideoRecording;
            // 非视频录像中，直接退出
            if (!videoRecording)
            {
                return;
            }

            if (e.Packet.Command == CtrlCmds.BUTTON)
            {
                var action = PacketHelper.DecodeButton(e.Packet);

                if (action == InstButtonActions.Click)
                {
                    KnownAppEvents.PublishRequireCaptureScreen();
                }
            }

            var experInfo = ActiveApp.Instance.MainWindowVm.ExperInfo;
            // 实验还在进行中
            ExperRunController.ReportState(experInfo.Exper.Exper);
        }
    }
}